﻿using Microsoft.Xna.Framework;
using RaptorEngine;
using RaptorEngine.Components;
using RaptorEngine.Components.CollidableComponent;

namespace OilRigSumoWrestlers.Actions
{
	internal class BouncableCube : CollisionAction
	{
		public BouncableCube(float remainingEnergy)
		{
			RemainingEnergy = new Vector3(remainingEnergy);
			RemainingEnergy.Y = 1;
		}

	    /// <summary>
	    /// Handles collision between two boundingSpheres with newton physics solid spheres and no energy wasted. 
	    /// </summary>
	    /// <param name="ownEntity">The parent entity of the action</param>
	    /// <param name="ownVolume">The parent bounding volume of the action</param>
	    /// <param name="othersEntity">The other colliding entity</param>
	    /// <param name="othersVolume">The boundingVolume of the other entity</param>
	    /// <param name="gameTime"></param>
	    public override void HandleCollision(Entity ownEntity, BoundingVolume ownVolume, Entity othersEntity,
                                    BoundingVolume othersVolume)
		{
			Vector3 dir = (othersEntity.Position - ownEntity.Position);

			// Move the entities away from eachother to avoid multiple collision where there only should be one.
			if (othersVolume.VolumeType == VolumeType.Sphere && othersVolume.CollisionTypes.Contains((int)CollisionType.Player))
			{
				dir.Normalize();

				Vector3 collisionPoint = othersEntity.Position - ownEntity.Position;
				collisionPoint = Clamp(collisionPoint, new Vector3(ownVolume.Radius));

				Vector3 collisionPointInWorld = collisionPoint + ownEntity.Position;
				float intersectLength = othersVolume.Radius - (collisionPointInWorld - othersEntity.Position).Length();

				dir.Y = 0;
				if (ownEntity.HasComponent(ComponentType.Movable))
				{
					ownEntity.Position += intersectLength*dir;
				}
				else if (othersEntity.HasComponent(ComponentType.Movable))
				{
					othersEntity.Position += intersectLength * dir;
				}
			}

			// Is the other entity a player
			if (othersEntity.HasComponent(ComponentType.Movable) && othersVolume.CollisionTypes.Contains((int)CollisionType.Player))
			{
				// Does it support particles
				if (othersEntity.HasComponent(ComponentType.Particle))
				{
					ParticleComp part = ((ParticleComp)othersEntity.GetComponent(ComponentType.Particle));

					// If the velocity is high enough create dust particle and play a sound
					for (int i = 0; i < UseParticels(ownEntity, othersEntity); i++)
					{
						if (i == 0)
						{
							//GameEngine.Instance.Audio.PlaySound("player_hit", .5f);
                            GameEngine.Instance.Audio.Play3DSound("player_hit", 1f, othersEntity.Position);
						}
						part.AddParticles("bouncingSmoke", othersEntity.Position - othersVolume.Radius * dir, Vector3.Zero);
					}
				}
				Movable otherMovable = (Movable)othersEntity.GetComponent(ComponentType.Movable);
				if (othersVolume.Center.Y < ownVolume.Box.Value.Min.Y)
				{
					otherMovable.Velocity = Vector3.Reflect(otherMovable.Velocity, Vector3.Down) * RemainingEnergy;
					othersVolume.IsCalculated = true;
				}
				else if (othersVolume.Center.Y > ownVolume.Box.Value.Max.Y)
				{
					otherMovable.Velocity = Vector3.Reflect(otherMovable.Velocity, Vector3.Up) * RemainingEnergy;
					othersVolume.IsCalculated = true;
				}
				
				else if (othersVolume.Center.X < ownVolume.Box.Value.Min.X && otherMovable.Velocity.X > 0)
				{
					otherMovable.Velocity = Vector3.Reflect(otherMovable.Velocity, Vector3.Left) * RemainingEnergy;
					othersVolume.IsCalculated = true;
				}
				else if (othersVolume.Center.X > ownVolume.Box.Value.Max.X && otherMovable.Velocity.X < 0)
				{
					otherMovable.Velocity = Vector3.Reflect(otherMovable.Velocity, Vector3.Right) * RemainingEnergy;
					othersVolume.IsCalculated = true;
				}

				else if (othersVolume.Center.Z < ownVolume.Box.Value.Min.Z && otherMovable.Velocity.Z > 0)
				{
					otherMovable.Velocity = Vector3.Reflect(otherMovable.Velocity, Vector3.Forward) * RemainingEnergy;
					othersVolume.IsCalculated = true;
				}
				else if (othersVolume.Center.Z > ownVolume.Box.Value.Max.Z && otherMovable.Velocity.Z < 0)
				{
					otherMovable.Velocity = Vector3.Reflect(otherMovable.Velocity, Vector3.Backward) * RemainingEnergy;
					othersVolume.IsCalculated = true;
				}
			}
		}

		/// <summary>
		/// Used to get the closest point between a cube and a sphere.
		/// </summary>
		/// <param name="collisionPoint">The distance and direction between the two colliding entities</param>
		/// <param name="max">The max values for the cube</param>
		/// <returns>The point where the sphere first collided with the sphere</returns>
		private Vector3 Clamp(Vector3 collisionPoint, Vector3 max)
		{
			collisionPoint.X = (collisionPoint.X < -max.X) ? -max.X : collisionPoint.X;
			collisionPoint.X = (collisionPoint.X > max.X) ? max.X : collisionPoint.X;
			collisionPoint.Y = (collisionPoint.Y < -max.Y) ? -max.Y : collisionPoint.Y;
			collisionPoint.Y = (collisionPoint.Y > max.Y) ? max.Y : collisionPoint.Y;
			collisionPoint.Z = (collisionPoint.Z < -max.Z) ? -max.Z : collisionPoint.Z;
			collisionPoint.Z = (collisionPoint.Z > max.Z) ? max.Z : collisionPoint.Z;
			return collisionPoint;
		}

		/// <summary>
		/// Is the total velocity high enough to create particles and how many particles will be created?
		/// </summary>
		/// <param name="ownEntity">The first colliding entity</param>
		/// <param name="othersEntity">The second colliding entity</param>
		/// <returns>The number of particles to be created</returns>
		private int UseParticels(Entity ownEntity, Entity othersEntity)
		{
			float totalVelocity = 0;
			if (ownEntity.HasComponent(ComponentType.Movable))
			{
				totalVelocity += ((Movable)ownEntity.GetComponent(ComponentType.Movable)).Velocity.Length();
			}
			if (othersEntity.HasComponent(ComponentType.Movable))
			{
				totalVelocity += ((Movable)othersEntity.GetComponent(ComponentType.Movable)).Velocity.Length();
			}

			totalVelocity = (totalVelocity < 0.008f) ? 0 : totalVelocity;

			return (int)(totalVelocity * 150);
		}
	}
}
